-
Notifications
You must be signed in to change notification settings - Fork 1.1k
[TT-15954] Make org session fetch non-blocking #7480
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
🔍 Code Analysis ResultsThis pull request introduces a non-blocking mechanism for fetching organization session data, significantly improving the gateway's resilience against slow or unavailable backend services. It prevents the request pipeline from being blocked by making session fetches timeout-protected and session expiry checks asynchronous on cache misses. Files Changed AnalysisThe changes span 11 files across the
Architecture & Impact AssessmentWhat this PR accomplishesThis PR decouples the gateway's request lifecycle from the performance of the backend session store. By making organization session fetches non-blocking, it prevents a slow or unavailable backend from causing cascading failures in the gateway, significantly improving system resilience and availability. Key technical changes introduced
Affected system components
Non-Blocking Org Session Expiry FlowThis diagram illustrates the new non-blocking flow for sequenceDiagram
participant Requester
participant Gateway Middleware
participant Expiry Cache (In-memory)
participant Session Store (e.g., Redis)
Requester->>Gateway Middleware: API Request (triggers OrgSessionExpiry)
Gateway Middleware->>Expiry Cache: Get expiry for OrgID
alt Cache Hit
Expiry Cache-->>Gateway Middleware: Return cached expiry
Gateway Middleware-->>Requester: Continue processing
else Cache Miss
Expiry Cache-->>Gateway Middleware: Not found
Gateway Middleware-->>Requester: Return default expiry immediately (non-blocking)
par Background Refresh
Gateway Middleware->>Gateway Middleware: go refreshOrgSessionExpiry()
Gateway Middleware->>Session Store: Fetch Org Session (with 2s timeout)
Session Store-->>Gateway Middleware: Return session data
Gateway Middleware->>Expiry Cache: Update cache with new expiry
end
end
Scope Discovery & Context Expansion
Metadata
Powered by Visor from Probelabs Last updated: 2025-11-06T18:15:09.798Z | Triggered by: synchronize | Commit: bc5f13a 💡 TIP: You can chat with Visor using |
🔍 Code Analysis ResultsSecurity Issues (2)
Architecture Issues (4)
Performance Issues (1)
Quality Issues (2)
✅ Dependency Check PassedNo dependency issues found – changes LGTM. ✅ Connectivity Check PassedNo connectivity issues found – changes LGTM. Powered by Visor from Probelabs Last updated: 2025-11-06T18:15:11.137Z | Triggered by: synchronize | Commit: bc5f13a 💡 TIP: You can chat with Visor using |
97939c6 to
b403371
Compare
|
API Changes --- prev.txt 2025-11-06 18:10:50.380947635 +0000
+++ current.txt 2025-11-06 18:10:40.352808671 +0000
@@ -8838,6 +8838,9 @@
UptimeAnalytics_KEYNAME = "tyk-uptime-analytics"
)
const (
+ DEFAULT_ORG_SESSION_EXPIRATION = int64(604800)
+)
+const (
ErrAuthAuthorizationFieldMissing = "auth.auth_field_missing"
ErrAuthKeyNotFound = "auth.key_not_found"
ErrAuthCertNotFound = "auth.cert_not_found"
@@ -8901,9 +8904,6 @@
const CoProcessDefaultKeyPrefix = "coprocess-data:"
CoProcessDefaultKeyPrefix is used as a key prefix for this CP.
-const (
- DEFAULT_ORG_SESSION_EXPIRATION = int64(604800)
-)
const EH_CoProcessHandler = event.CoProcessHandler
EH_CoProcessHandler is used for event system, maintained here for backwards
compatibility.
@@ -9696,6 +9696,10 @@
SessionDetail returns the session detail using the storage engine (either in
memory or Redis)
+func (b *DefaultSessionManager) SessionDetailContext(ctx context.Context, orgID string, keyName string, hashed bool) (user.SessionState, bool)
+ SessionDetailContext returns the session detail using the storage engine
+ with context support for cancellation
+
func (b *DefaultSessionManager) Sessions(filter string) []string
Sessions returns all sessions in the key store that match a filter key (a
prefix)
@@ -10628,6 +10632,9 @@
func (l *LDAPStorageHandler) GetKey(filter string) (string, error)
+func (l *LDAPStorageHandler) GetKeyContext(ctx context.Context, keyName string) (string, error)
+ GetKeyContext retrieves a key with context support.
+
func (l LDAPStorageHandler) GetKeyPrefix() string
func (l *LDAPStorageHandler) GetKeys(filter string) []string
@@ -10640,8 +10647,14 @@
func (r *LDAPStorageHandler) GetMultiKey(keyNames []string) ([]string, error)
+func (l *LDAPStorageHandler) GetMultiKeyContext(ctx context.Context, keyNames []string) ([]string, error)
+ GetMultiKeyContext retrieves multiple keys with context support.
+
func (l *LDAPStorageHandler) GetRawKey(filter string) (string, error)
+func (l *LDAPStorageHandler) GetRawKeyContext(ctx context.Context, keyName string) (string, error)
+ GetRawKeyContext retrieves a raw key with context support.
+
func (l *LDAPStorageHandler) GetRollingWindow(keyName string, per int64, pipeline bool) (int, []interface{})
func (l LDAPStorageHandler) GetSet(keyName string) (map[string]string, error)
@@ -11107,6 +11120,9 @@
func (r *RPCStorageHandler) GetKey(keyName string) (string, error)
GetKey will retrieve a key from the database
+func (r *RPCStorageHandler) GetKeyContext(ctx context.Context, keyName string) (string, error)
+ GetKeyContext retrieves a key with context support.
+
func (r *RPCStorageHandler) GetKeyPrefix() string
func (r *RPCStorageHandler) GetKeys(filter string) []string
@@ -11125,11 +11141,17 @@
func (r *RPCStorageHandler) GetMultiKey(keyNames []string) ([]string, error)
+func (r *RPCStorageHandler) GetMultiKeyContext(ctx context.Context, keyNames []string) ([]string, error)
+ GetMultiKeyContext retrieves multiple keys with context support.
+
func (r *RPCStorageHandler) GetPolicies(orgId string) string
GetPolicies will pull Policies from the RPC server
func (r *RPCStorageHandler) GetRawKey(keyName string) (string, error)
+func (r *RPCStorageHandler) GetRawKeyContext(ctx context.Context, keyName string) (string, error)
+ GetRawKeyContext retrieves a raw key with context support.
+
func (r *RPCStorageHandler) GetRollingWindow(keyName string, per int64, pipeline bool) (int, []interface{})
func (r RPCStorageHandler) GetSet(keyName string) (map[string]string, error)
@@ -11693,6 +11715,7 @@
UpdateSession(keyName string, session *user.SessionState, resetTTLTo int64, hashed bool) error
RemoveSession(orgID string, keyName string, hashed bool) bool
SessionDetail(orgID string, keyName string, hashed bool) (user.SessionState, bool)
+ SessionDetailContext(ctx context.Context, orgID string, keyName string, hashed bool) (user.SessionState, bool)
KeyExpired(newSession *user.SessionState) bool
Sessions(filter string) []string
ResetQuota(string, *user.SessionState, bool)
@@ -13208,6 +13231,10 @@
GetKey retrieves the value for a given key from DummyStorage, or an error if
not found.
+func (s *DummyStorage) GetKeyContext(_ context.Context, key string) (string, error)
+ GetKeyContext retrieves the value for a given key with context support
+ (delegates to GetKey).
+
func (s *DummyStorage) GetKeyPrefix() string
GetKeyPrefix returns the prefix used for keys in DummyStorage; not yet
implemented.
@@ -13233,6 +13260,10 @@
of keys. It returns a slice of strings containing the values corresponding
to each provided key, and an error if the operation cannot be completed.
+func (s *DummyStorage) GetMultiKeyContext(_ context.Context, keys []string) ([]string, error)
+ GetMultiKeyContext retrieves multiple keys with context support (delegates
+ to GetMultiKey).
+
func (s *DummyStorage) GetRawKey(key string) (string, error)
GetRawKey retrieves the value associated with a given key from the
DummyStorage. The method accepts a single string as the key and returns the
@@ -13240,6 +13271,10 @@
the retrieval was successful. Currently, this method is not implemented and
will cause a panic if invoked.
+func (s *DummyStorage) GetRawKeyContext(_ context.Context, key string) (string, error)
+ GetRawKeyContext retrieves a raw key value with context support (delegates
+ to GetRawKey).
+
func (s *DummyStorage) GetRollingWindow(string, int64, bool) (int, []interface{})
GetRollingWindow retrieves data for a specified rolling window; currently
not implemented.
@@ -13323,6 +13358,10 @@
RemoveFromList(string, string) error
AppendToSet(string, string)
Exists(string) (bool, error)
+
+ GetKeyContext(ctx context.Context, key string) (string, error)
+ GetRawKeyContext(ctx context.Context, key string) (string, error)
+ GetMultiKeyContext(ctx context.Context, keys []string) ([]string, error)
}
Handler is a standard interface to a storage backend, used by
AuthorisationManager to read and write key values to the backend
@@ -13364,6 +13403,9 @@
func (m MdcbStorage) GetKey(key string) (string, error)
+func (m MdcbStorage) GetKeyContext(ctx context.Context, key string) (string, error)
+ GetKeyContext retrieves a key with context support.
+
func (m MdcbStorage) GetKeyPrefix() string
func (m MdcbStorage) GetKeys(key string) []string
@@ -13377,8 +13419,14 @@
func (m MdcbStorage) GetMultiKey(keyNames []string) ([]string, error)
GetMultiKey gets multiple keys from the MDCB layer
+func (m MdcbStorage) GetMultiKeyContext(ctx context.Context, keys []string) ([]string, error)
+ GetMultiKeyContext retrieves multiple keys with context support.
+
func (m MdcbStorage) GetRawKey(string) (string, error)
+func (m MdcbStorage) GetRawKeyContext(ctx context.Context, key string) (string, error)
+ GetRawKeyContext retrieves a raw key with context support.
+
func (m MdcbStorage) GetRollingWindow(key string, per int64, pipeline bool) (int, []interface{})
func (m MdcbStorage) GetSet(key string) (map[string]string, error)
@@ -13466,6 +13514,10 @@
func (r *RedisCluster) GetKey(keyName string) (string, error)
GetKey will retrieve a key from the database
+func (r *RedisCluster) GetKeyContext(ctx context.Context, keyName string) (string, error)
+ GetKeyContext will retrieve a key from the database with context support for
+ cancellation
+
func (r *RedisCluster) GetKeyPrefix() string
GetPrefix returns storage key prefix
@@ -13489,8 +13541,16 @@
func (r *RedisCluster) GetMultiKey(keys []string) ([]string, error)
GetMultiKey gets multiple keys from the database
+func (r *RedisCluster) GetMultiKeyContext(ctx context.Context, keys []string) ([]string, error)
+ GetMultiKeyContext gets multiple keys from the database with context support
+ for cancellation
+
func (r *RedisCluster) GetRawKey(keyName string) (string, error)
+func (r *RedisCluster) GetRawKeyContext(ctx context.Context, keyName string) (string, error)
+ GetRawKeyContext will retrieve a raw key from the database with context
+ support for cancellation
+
func (r *RedisCluster) GetRollingWindow(keyName string, per int64, pipeline bool) (int, []interface{})
func (r *RedisCluster) GetSet(keyName string) (map[string]string, error)
@@ -13627,6 +13687,53 @@
TYPES
+type MockAnalyticsHandler struct {
+ // Has unexported fields.
+}
+ MockAnalyticsHandler is a mock of AnalyticsHandler interface.
+
+func NewMockAnalyticsHandler(ctrl *gomock.Controller) *MockAnalyticsHandler
+ NewMockAnalyticsHandler creates a new mock instance.
+
+func (m *MockAnalyticsHandler) AppendToSetPipelined(arg0 string, arg1 [][]byte)
+ AppendToSetPipelined mocks base method.
+
+func (m *MockAnalyticsHandler) Connect() bool
+ Connect mocks base method.
+
+func (m *MockAnalyticsHandler) EXPECT() *MockAnalyticsHandlerMockRecorder
+ EXPECT returns an object that allows the caller to indicate expected use.
+
+func (m *MockAnalyticsHandler) GetAndDeleteSet(arg0 string) []interface{}
+ GetAndDeleteSet mocks base method.
+
+func (m *MockAnalyticsHandler) GetExp(arg0 string) (int64, error)
+ GetExp mocks base method.
+
+func (m *MockAnalyticsHandler) SetExp(arg0 string, arg1 int64) error
+ SetExp mocks base method.
+
+type MockAnalyticsHandlerMockRecorder struct {
+ // Has unexported fields.
+}
+ MockAnalyticsHandlerMockRecorder is the mock recorder for
+ MockAnalyticsHandler.
+
+func (mr *MockAnalyticsHandlerMockRecorder) AppendToSetPipelined(arg0, arg1 interface{}) *gomock.Call
+ AppendToSetPipelined indicates an expected call of AppendToSetPipelined.
+
+func (mr *MockAnalyticsHandlerMockRecorder) Connect() *gomock.Call
+ Connect indicates an expected call of Connect.
+
+func (mr *MockAnalyticsHandlerMockRecorder) GetAndDeleteSet(arg0 interface{}) *gomock.Call
+ GetAndDeleteSet indicates an expected call of GetAndDeleteSet.
+
+func (mr *MockAnalyticsHandlerMockRecorder) GetExp(arg0 interface{}) *gomock.Call
+ GetExp indicates an expected call of GetExp.
+
+func (mr *MockAnalyticsHandlerMockRecorder) SetExp(arg0, arg1 interface{}) *gomock.Call
+ SetExp indicates an expected call of SetExp.
+
type MockHandler struct {
// Has unexported fields.
}
@@ -13674,7 +13781,7 @@
func (m *MockHandler) Exists(arg0 string) (bool, error)
Exists mocks base method.
-func (m *MockHandler) GetAndDeleteSet(arg0 string) []any
+func (m *MockHandler) GetAndDeleteSet(arg0 string) []interface{}
GetAndDeleteSet mocks base method.
func (m *MockHandler) GetExp(arg0 string) (int64, error)
@@ -13683,6 +13790,9 @@
func (m *MockHandler) GetKey(arg0 string) (string, error)
GetKey mocks base method.
+func (m *MockHandler) GetKeyContext(ctx context.Context, key string) (string, error)
+ GetKeyContext mocks base method.
+
func (m *MockHandler) GetKeyPrefix() string
GetKeyPrefix mocks base method.
@@ -13701,10 +13811,16 @@
func (m *MockHandler) GetMultiKey(arg0 []string) ([]string, error)
GetMultiKey mocks base method.
+func (m *MockHandler) GetMultiKeyContext(ctx context.Context, keys []string) ([]string, error)
+ GetMultiKeyContext mocks base method.
+
func (m *MockHandler) GetRawKey(arg0 string) (string, error)
GetRawKey mocks base method.
-func (m *MockHandler) GetRollingWindow(key string, per int64, pipeline bool) (int, []any)
+func (m *MockHandler) GetRawKeyContext(ctx context.Context, key string) (string, error)
+ GetRawKeyContext mocks base method.
+
+func (m *MockHandler) GetRollingWindow(key string, per int64, pipeline bool) (int, []interface{})
GetRollingWindow mocks base method.
func (m *MockHandler) GetSet(arg0 string) (map[string]string, error)
@@ -13734,7 +13850,7 @@
func (m *MockHandler) SetRawKey(arg0, arg1 string, arg2 int64) error
SetRawKey mocks base method.
-func (m *MockHandler) SetRollingWindow(key string, per int64, val string, pipeline bool) (int, []any)
+func (m *MockHandler) SetRollingWindow(key string, per int64, val string, pipeline bool) (int, []interface{})
SetRollingWindow mocks base method.
type MockHandlerMockRecorder struct {
@@ -13742,104 +13858,113 @@
}
MockHandlerMockRecorder is the mock recorder for MockHandler.
-func (mr *MockHandlerMockRecorder) AddToSet(arg0, arg1 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) AddToSet(arg0, arg1 interface{}) *gomock.Call
AddToSet indicates an expected call of AddToSet.
-func (mr *MockHandlerMockRecorder) AddToSortedSet(arg0, arg1, arg2 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) AddToSortedSet(arg0, arg1, arg2 interface{}) *gomock.Call
AddToSortedSet indicates an expected call of AddToSortedSet.
-func (mr *MockHandlerMockRecorder) AppendToSet(arg0, arg1 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) AppendToSet(arg0, arg1 interface{}) *gomock.Call
AppendToSet indicates an expected call of AppendToSet.
func (mr *MockHandlerMockRecorder) Connect() *gomock.Call
Connect indicates an expected call of Connect.
-func (mr *MockHandlerMockRecorder) Decrement(arg0 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) Decrement(arg0 interface{}) *gomock.Call
Decrement indicates an expected call of Decrement.
func (mr *MockHandlerMockRecorder) DeleteAllKeys() *gomock.Call
DeleteAllKeys indicates an expected call of DeleteAllKeys.
-func (mr *MockHandlerMockRecorder) DeleteKey(arg0 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) DeleteKey(arg0 interface{}) *gomock.Call
DeleteKey indicates an expected call of DeleteKey.
-func (mr *MockHandlerMockRecorder) DeleteKeys(arg0 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) DeleteKeys(arg0 interface{}) *gomock.Call
DeleteKeys indicates an expected call of DeleteKeys.
-func (mr *MockHandlerMockRecorder) DeleteRawKey(arg0 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) DeleteRawKey(arg0 interface{}) *gomock.Call
DeleteRawKey indicates an expected call of DeleteRawKey.
-func (mr *MockHandlerMockRecorder) DeleteRawKeys(arg0 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) DeleteRawKeys(arg0 interface{}) *gomock.Call
DeleteRawKeys indicates an expected call of DeleteRawKeys.
-func (mr *MockHandlerMockRecorder) DeleteScanMatch(arg0 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) DeleteScanMatch(arg0 interface{}) *gomock.Call
DeleteScanMatch indicates an expected call of DeleteScanMatch.
-func (mr *MockHandlerMockRecorder) Exists(arg0 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) Exists(arg0 interface{}) *gomock.Call
Exists indicates an expected call of Exists.
-func (mr *MockHandlerMockRecorder) GetAndDeleteSet(arg0 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) GetAndDeleteSet(arg0 interface{}) *gomock.Call
GetAndDeleteSet indicates an expected call of GetAndDeleteSet.
-func (mr *MockHandlerMockRecorder) GetExp(arg0 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) GetExp(arg0 interface{}) *gomock.Call
GetExp indicates an expected call of GetExp.
-func (mr *MockHandlerMockRecorder) GetKey(arg0 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) GetKey(arg0 interface{}) *gomock.Call
GetKey indicates an expected call of GetKey.
+func (mr *MockHandlerMockRecorder) GetKeyContext(ctx, key interface{}) *gomock.Call
+ GetKeyContext indicates an expected call of GetKeyContext.
+
func (mr *MockHandlerMockRecorder) GetKeyPrefix() *gomock.Call
GetKeyPrefix indicates an expected call of GetKeyPrefix.
-func (mr *MockHandlerMockRecorder) GetKeys(arg0 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) GetKeys(arg0 interface{}) *gomock.Call
GetKeys indicates an expected call of GetKeys.
func (mr *MockHandlerMockRecorder) GetKeysAndValues() *gomock.Call
GetKeysAndValues indicates an expected call of GetKeysAndValues.
-func (mr *MockHandlerMockRecorder) GetKeysAndValuesWithFilter(arg0 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) GetKeysAndValuesWithFilter(arg0 interface{}) *gomock.Call
GetKeysAndValuesWithFilter indicates an expected call of
GetKeysAndValuesWithFilter.
-func (mr *MockHandlerMockRecorder) GetListRange(arg0, arg1, arg2 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) GetListRange(arg0, arg1, arg2 interface{}) *gomock.Call
GetListRange indicates an expected call of GetListRange.
-func (mr *MockHandlerMockRecorder) GetMultiKey(arg0 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) GetMultiKey(arg0 interface{}) *gomock.Call
GetMultiKey indicates an expected call of GetMultiKey.
-func (mr *MockHandlerMockRecorder) GetRawKey(arg0 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) GetMultiKeyContext(ctx, keys interface{}) *gomock.Call
+ GetMultiKeyContext indicates an expected call of GetMultiKeyContext.
+
+func (mr *MockHandlerMockRecorder) GetRawKey(arg0 interface{}) *gomock.Call
GetRawKey indicates an expected call of GetRawKey.
-func (mr *MockHandlerMockRecorder) GetRollingWindow(key, per, pipeline any) *gomock.Call
+func (mr *MockHandlerMockRecorder) GetRawKeyContext(ctx, key interface{}) *gomock.Call
+ GetRawKeyContext indicates an expected call of GetRawKeyContext.
+
+func (mr *MockHandlerMockRecorder) GetRollingWindow(key, per, pipeline interface{}) *gomock.Call
GetRollingWindow indicates an expected call of GetRollingWindow.
-func (mr *MockHandlerMockRecorder) GetSet(arg0 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) GetSet(arg0 interface{}) *gomock.Call
GetSet indicates an expected call of GetSet.
-func (mr *MockHandlerMockRecorder) GetSortedSetRange(arg0, arg1, arg2 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) GetSortedSetRange(arg0, arg1, arg2 interface{}) *gomock.Call
GetSortedSetRange indicates an expected call of GetSortedSetRange.
-func (mr *MockHandlerMockRecorder) IncrememntWithExpire(arg0, arg1 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) IncrememntWithExpire(arg0, arg1 interface{}) *gomock.Call
IncrememntWithExpire indicates an expected call of IncrememntWithExpire.
-func (mr *MockHandlerMockRecorder) RemoveFromList(arg0, arg1 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) RemoveFromList(arg0, arg1 interface{}) *gomock.Call
RemoveFromList indicates an expected call of RemoveFromList.
-func (mr *MockHandlerMockRecorder) RemoveFromSet(arg0, arg1 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) RemoveFromSet(arg0, arg1 interface{}) *gomock.Call
RemoveFromSet indicates an expected call of RemoveFromSet.
-func (mr *MockHandlerMockRecorder) RemoveSortedSetRange(arg0, arg1, arg2 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) RemoveSortedSetRange(arg0, arg1, arg2 interface{}) *gomock.Call
RemoveSortedSetRange indicates an expected call of RemoveSortedSetRange.
-func (mr *MockHandlerMockRecorder) SetExp(arg0, arg1 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) SetExp(arg0, arg1 interface{}) *gomock.Call
SetExp indicates an expected call of SetExp.
-func (mr *MockHandlerMockRecorder) SetKey(arg0, arg1, arg2 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) SetKey(arg0, arg1, arg2 interface{}) *gomock.Call
SetKey indicates an expected call of SetKey.
-func (mr *MockHandlerMockRecorder) SetRawKey(arg0, arg1, arg2 any) *gomock.Call
+func (mr *MockHandlerMockRecorder) SetRawKey(arg0, arg1, arg2 interface{}) *gomock.Call
SetRawKey indicates an expected call of SetRawKey.
-func (mr *MockHandlerMockRecorder) SetRollingWindow(key, per, val, pipeline any) *gomock.Call
+func (mr *MockHandlerMockRecorder) SetRollingWindow(key, per, val, pipeline interface{}) *gomock.Call
SetRollingWindow indicates an expected call of SetRollingWindow.
# Package: ./tcp |
b85f84a to
3ba213d
Compare
|
/visor I doubt about those panic protections, is there anything in this code which can actually cause panic? I would rather solve source of panics. |
🔍 Code Analysis ResultsYou've raised a great point. Ideally, we should always fix the root cause of a panic rather than just recovering from it. The panic handlers here are added as a defensive measure to improve the gateway's resilience. The code calls Since these session fetches now run in background goroutines, an unrecovered panic would crash the entire gateway process. The In short, this is a safeguard against potential failures in external dependencies, rather than a workaround for known panics in our own code. References: Powered by Visor from Probelabs Last updated: 2025-11-04T20:22:28.534Z | Triggered by: comment by @buger 💡 TIP: You can chat with Visor using |
|
/visor re recovery statements, but if it happens inside goroutine, it will not break the main request path, only goroutine will fail, right? |
🔍 Code Analysis ResultsYes, you are absolutely correct. Your understanding is spot on. A panic only unwinds the stack of the goroutine in which it occurs. By placing a In this PR, that's exactly the pattern being used:
This design intentionally isolates the main request path from potential failures in these background operations, which is a key goal of this change. References: Powered by Visor from Probelabs Last updated: 2025-11-04T20:26:35.530Z | Triggered by: comment by @buger 💡 TIP: You can chat with Visor using |
🎯 Recommended Merge TargetsBased on JIRA ticket TT-15954: Request pipeline blocked by synchronous RPC calls every 10 minutes when MDCB unavailable Fix Version: Tyk 5.11.0
Required:
Fix Version: Tyk 5.8.9Required:
📋 Workflow
|
|
|
/visor ask what are my options for handling this issue that you found, and how likely is this issue to occur? I have quoted it below:
|
🔍 Code Analysis ResultsAn excellent question. You've correctly identified a critical risk with the current implementation. Here is an analysis of the issue, your options, and the likelihood of occurrence. The Core ProblemThe analysis is correct. The The underlying Your Options for Handling This
Likelihood of OccurrenceThe likelihood of this issue causing a problem depends entirely on the stability of your backend session store (Redis/MDCB) and the network between it and the gateway.
In these failure states, every API request that triggers an org session fetch would spawn a goroutine that leaks. With sustained traffic, this would lead to unbounded memory and goroutine growth, eventually crashing the gateway. While it's an edge case, its impact is critical when it happens. In summary, your current PR fixes the immediate problem of blocked requests but introduces a new, more subtle risk of resource exhaustion during backend outages. The best long-term solution is to make the storage layer context-aware (Option 1). References: Powered by Visor from Probelabs Last updated: 2025-11-06T13:06:49.494Z | Triggered by: comment by @imogenkraak 💡 TIP: You can chat with Visor using |
e3b3b39 to
cf68026
Compare
|
/visor in response to this issue: The context-aware methods added to the I am unable to pass context into the RPC library, so I can't address this issue. Please can you analyse how likely are leaks and is there a way around this. |
🔍 Code Analysis ResultsYou've correctly identified the core issue. The context is not being propagated in the Likelihood and ImpactThe goroutine leak will only occur if a backend service (MDCB, RPC, or LDAP) becomes unresponsive and fails to return a response or error.
While the trigger event (a hanging backend) may be rare, the impact is high, so it's a critical issue to address. How to Fix ThisYou mentioned being unable to pass context into the RPC library. It is possible to update the underlying network clients to be context-aware. This is the recommended way to prevent these leaks.
These changes would properly propagate the context and ensure that the goroutines are cleaned up immediately when the timeout is reached, fully resolving the potential for leaks. References: Powered by Visor from Probelabs Last updated: 2025-11-07T11:57:42.950Z | Triggered by: comment by @imogenkraak 💡 TIP: You can chat with Visor using |
|
/visor I am not sure what you mean to replace. sendCommand func does not exist in this file or repository |
🔍 Code Analysis ResultsYou are absolutely right, and I sincerely apologize for the confusion and the incorrect suggestions. My analysis was flawed. You were correct that you cannot simply pass a context down, and my search for Thank you for your patience. I've now traced the full call stack, and the issue is more fundamental. Here is the correct analysis:
The root of the problem is that the underlying RPC client ( This means that even though your Conclusion & Path Forward: Your implementation correctly solves the primary problem: it prevents the main API request pipeline from blocking. The risk of goroutines getting stuck for 30 seconds is a limitation of the underlying A complete fix would require modifying the I am sorry for the incorrect guidance earlier. Your initial assessment was accurate. References: Powered by Visor from Probelabs Last updated: 2025-11-07T12:11:31.710Z | Triggered by: comment by @imogenkraak 💡 TIP: You can chat with Visor using |




Description
Implemented non-blocking org session expiry with emergency mode awareness and timeout protection.
Related Issue
TT-15954
Motivation and Context
How This Has Been Tested
Screenshots (if appropriate)
Types of changes
Checklist
Ticket Details
TT-15954
Generated at: 2025-11-06 18:09:58